#ifndef __UTIL_H__
#define __UTIL_H__
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <time.h>
#include <sys/time.h>
#include <iomanip>
#include <string.h>
#include "base64.h"
#include "aes.h"
#include <string.h>
#include <ctype.h>
#include "json.h"
#include <assert.h>
#include <limits>
#include "global.h"
#include <google/protobuf/message.h>
#include <google/protobuf/util/json_util.h>



enum ExportType
{
	EXPORT_TYPE_NONE = 0,
	EXPORT_TYPE_BASE64 = 1,
	EXPORT_TYPE_HEX = 2,
	EXPORT_TYPE_BASE64_URL = 3,
};


class CTools
{
    public:
        static int64_t GetTickCount()
        {
            timeval timesnow;
            struct timezone tmpzone;
            gettimeofday(&timesnow,&tmpzone);
            return ((int64_t)(timesnow.tv_sec*1000) + (int64_t)(timesnow.tv_usec/1000));
        }

        static uint64_t GetUnixTime()
        {
            timeval timesnow;
            struct timezone tmpzone;
            gettimeofday(&timesnow,&tmpzone);
            return timesnow.tv_sec;
        }

        static uint64_t GetUnixTimeMs()
        {
            timeval timesnow;
            gettimeofday(&timesnow, NULL);
            return (timesnow.tv_sec * 1000000 + timesnow.tv_usec);
        }
		
		static int GetDateStr(std::string &str)
		{
			char now[64] = {0};
			time_t tt = time(NULL);
			struct tm *ttime;
			ttime = localtime(&tt);
			strftime(now, sizeof(now), "%Y%m%d", ttime);
            str = now;
			return 0;
		}

        static int split_str(const char* ps_str, const char* ps_sp, std::vector<std::string> &v_ret)
        {
            char* ps_temp;
            char* p;
            int i_len = (int)strlen(ps_str);
            std::string st_str;
            ps_temp = new char[i_len + 2];
            snprintf(ps_temp, i_len + 1, "%s", ps_str);
            char *last = NULL;    p = strtok_r(ps_temp, ps_sp, &last);
            if (NULL == p)
            {
                delete ps_temp;
                return 0;
            }
            st_str = (std::string)p;
            v_ret.push_back(st_str);
            while (NULL != (p = strtok_r(NULL, ps_sp, &last)))
            {
                st_str = (std::string)p;
                v_ret.push_back(st_str);
            }
            delete ps_temp;
            return 0;
        }

        static std::map<std::string, std::string> ParseUrlParams(std::string params_str)
        {
            std::map<std::string, std::string> params;

            char* s = (char*)params_str.c_str();
            char* e = s + params_str.size();
            char* p = s;
            char* key_s = s;
            char* key_e = s;
            char* val_s = s;
            char* val_e = s;
            char word = '&';
            char sep = '=';
            while (p <= e)
            {
                if (p == e)
                {
                    if (key_s != val_s)
                    {
                        val_e = p;
                        params[std::string(key_s, key_e - key_s)] = std::string(val_s, val_e - val_s);
                    }

                    break;
                }
                else if (*p == word)
                {
                    if (key_s != val_s)
                    {
                        val_e = p;
                        params[std::string(key_s, key_e - key_s)] = std::string(val_s, val_e - val_s);
                    }

                    // 重置区间
                    key_s = key_e = val_s = val_e = p + 1;
                }
                else if (*p == sep)
                {
                    key_e = p;
                    val_s = p + 1;
                }
                else
                {
                    // 
                }
                p++;
            }

            return params;
        }

        static bool isSameDay(time_t time1, time_t time2)
        {
            return (((time1 + 28800) / 86400) == ((time2 + 28800) / 86400));
        }

        static std::string Base64(const std::string& str, bool encode = true)
        {
            if (encode)
            {
                size_t len = Base64encode_len(str.size());
                std::string buf;
                buf.resize(len);
                int d_len = Base64encode((char*)buf.data(), str.c_str(), str.size());
                if (d_len > 0)
                {
                    buf = buf.substr(0, d_len);
                }
                return buf;
            }
            else 
            {	
                size_t len = Base64decode_len(str.c_str());
                std::string buf;
                buf.resize(len);
                int d_len = Base64decode((char*)buf.data(), str.c_str());
                if (d_len > 0)
                {
                    buf = buf.substr(0, d_len);
                }
                return buf;
            }
        }
        static std::string ToHexString(const std::string& str) {
            std::stringstream ss;
            for (size_t i = 0; i < str.size(); i++)
            {
                ss << std::setw(2)
                    << std::setfill('0')
                    << std::hex
                    << static_cast<int>(static_cast<unsigned char>(str.at(i)));
            }
            return ss.str();
        }
        static std::string FromHexString(const std::string& str) {
            if (str.size() % 2 != 0)
            {
                return "";
            }
            bool has_error = false;
            auto to_v = [&](char c)->std::int8_t{
                if (c >= '0' && c <= '9')
                {
                    return c - '0';
                }
                else if (c >= 'a' && c <= 'f')
                {
                    return c - 'a' + 10;
                }
                else if (c >= 'A' && c <= 'F')
                {
                    return c - 'A' + 10;
                }
                has_error = true;
                return 0;
            };

            std::string ss;
            ss.resize(str.size() / 2);
            for (size_t i = 0; i < ss.size(); i++)
            {
                std::int8_t v = to_v(str.at(i * 2));
                v <<= 4;
                v |= to_v(str.at(i * 2 + 1));
                ss[i] = v;
                
                if (has_error)
                {
                    return "";
                }
            }
            return ss;
        }
        static std::string Hex(const std::string& str, bool encode = true)
        {
            if (encode)
            {
                return ToHexString(str);
            }
            else
            {
                return FromHexString(str);
            }
        }

        static std::string Base64Url(const std::string& str, bool encode = true)
        {
            /*
            BASE64URL编码的流程：1、明文使用BASE64进行加密 2、在BASE64的基础上进行一下的编码：2.1)去除尾部的"=" 2.2)把"+"替换成"-" 2.3)把"/"替换成"_"
            BASE64URL解码的流程：1)把"-"替换成"+". 2)把"_"替换成"/" . 3)(计算BASE64URL编码长度)%4 a)结果为0，不做处理 b)结果为2，字符串添加"==" c)结果为3，字符串添加"="
            */
            if (encode)
            {
                std::string data = Base64(str, encode);
                // trim
                size_t len = 0;
                for (; len < data.size(); len++)
                {
                    if (data.at(data.size() - len - 1) != '=') break;
                }
                if (len < 0 || len >= data.size())
                {
                    return data;
                }
                data = data.substr(0, data.size() - len);
                for (size_t i = 0; i < data.size(); i++)
                {
                    switch (data.at(i))
                    {
                    case '+':
                        data[i] = '-';
                        break;
                    case '/':
                        data[i] = '_';
                        break;
                    default:
                        break;
                    }
                }
                return data;
            }
            else
            {
                std::string data = str;
                for (size_t i = 0; i < data.size(); i++)
                {
                    switch (data.at(i))
                    {
                    case '-':
                        data[i] = '+';
                        break;
                    case '_':
                        data[i] = '/';
                        break;
                    default:
                        break;
                    }
                }
                const int size = 16;
                int len_trim = size - (data.size() % size);
                for (int i = 0; i < len_trim; i++)
                {
                    data += '=';
                }
                return Base64(data, encode);
            }
        }

        static std::string ExportString(const std::string& data, bool encode = true, ExportType et = EXPORT_TYPE_BASE64)
        {
            switch (et)
            {
            case EXPORT_TYPE_BASE64:
                return Base64(data, encode);
            case EXPORT_TYPE_HEX:
                return Hex(data, encode);
            case EXPORT_TYPE_BASE64_URL:
                return Base64Url(data, encode);
            default:
                break;
            }
            return data;
        }

        static std::string AesEcb(const std::string& key, const std::string& data, bool encode, ExportType et = EXPORT_TYPE_BASE64, Aes::Padding padding = Aes::PADDING_PKCS7) 
        {
            if (encode)
            {
                std::string data_out;
                if (!Aes::ecb_encrypt(data, data_out, key, true, padding))
                {
                    return "";
                }
                return ExportString(data_out, true, et);
            }
            else
            {
                std::string data_in = ExportString(data, false, et);
                std::string data_out = "";
                if (!Aes::ecb_encrypt(data_in, data_out, key, false, padding))
                {
                    return "";
                }
                return data_out;
            }
        }
        static std::string AesCbc(const std::string& key, const std::string& iv, const std::string& data, bool encode, ExportType et = EXPORT_TYPE_BASE64, Aes::Padding padding = Aes::PADDING_PKCS7) 
        {
            if (encode)
            {
                std::string data_out;
                if (!Aes::cbc_encrypt(data, data_out, key, iv, true, padding))
                {
                    return "";
                }
                return ExportString(data_out, true, et);
            }
            else
            {
                std::string data_in = ExportString(data, false, et);
                std::string data_out = "";
                if (!Aes::cbc_encrypt(data_in, data_out, key, iv, false, padding))
                {
                    return "";
                }
                return data_out;
            }
        }

        static std::string LoadFileData(std::string filename)
        {
            std::ifstream ifs(filename, std::ios::binary | std::ios::in);
            if (!ifs.is_open())
            {
                return "";
            }
            std::stringstream ss;
            ss << ifs.rdbuf();
            return ss.str();
        }

        template <typename T>
        static std::stringstream& MergeStream(std::stringstream& ss, T& t)
        {
            ss << t;
            return ss;
        }
        template <typename T, typename... Args, char seq = ':'>
        static std::stringstream& MergeStream(std::stringstream& ss, T& t, Args... args)
        {
            MergeStream(ss, args...);
            ss << seq << t;
            return ss;
        }

        static std::string ToUpStr(std::string str)
        {
            int distance = 'A' - 'a';
            for (auto & ch : str)
            {
                if (ch >= 'a' && ch <= 'z')
                {
                    ch += distance;
                }
            }
            return str;
        }
        static std::string ToLowStr(std::string str)
        {
            int distance = 'a' - 'A';
            for (auto & ch : str)
            {
                if (ch >= 'A' && ch <= 'Z')
                {
                    ch += distance;
                }
            }
            return str;
        }

        /*
         只处理了'+'字符和'%'字符的情况。对于更复杂的情况，解码可能需要更多的处理。
        */
        static Json::Value urlencoded_to_json(const std::string &urlencoded) {
            std::istringstream iss(urlencoded);
            Json::Value json_obj;
 
            for (std::string key_value; std::getline(iss, key_value, '&');) {
                auto pos = key_value.find('=');
                if (pos != std::string::npos) {
                    std::string key = key_value.substr(0, pos);
                    std::string value = key_value.substr(pos + 1);
                    // Decode URL-encoded string
                    // for (size_t i = 0; i < value.size(); ++i) {
                    //     if (value[i] == '+') {
                    //         value[i] = ' ';
                    //     } else if (value[i] == '%') {
                    //         int val = 0;
                    //         sscanf(value.c_str() + i + 1, "%2x", &val);
                    //         value[i] = static_cast<char>(val);
                    //         value.erase(i + 1, 2);
                    //     }
                    // }
                    json_obj[key] = value;
                }
            }
            return json_obj;
        }

        static unsigned char ToHex(unsigned char x)   
        {   
            return  x > 9 ? x + 55 : x + 48;   
        }  
        
        static unsigned char FromHex(unsigned char x)   
        {   
            unsigned char y;  
            if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;  
            else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;  
            else if (x >= '0' && x <= '9') y = x - '0';  
            else assert(0);  
            return y;  
        }  
  
        static std::string UrlEncode(const std::string& str)  
        {  
            std::string strTemp = "";  
            size_t length = str.length();  
            for (size_t i = 0; i < length; i++)  
            {  
                if (isalnum((unsigned char)str[i]) ||
                    (str[i] == '-') ||  
                    (str[i] == '_') ||   
                    (str[i] == '.') ||   
                    (str[i] == '~'))  
                    strTemp += str[i];  
                else if (str[i] == ' ')  
                    strTemp += "+";  
                else  
                {  
                    strTemp += '%';  
                    strTemp += ToHex((unsigned char)str[i] >> 4);  
                    strTemp += ToHex((unsigned char)str[i] % 16);  
                }  
            }  
            return strTemp;  
        }  
        
        static std::string UrlDecode(const std::string& str)  
        {  
            std::string strTemp = "";  
            size_t length = str.length();  
            for (size_t i = 0; i < length; i++)  
            {  
                if (str[i] == '+') strTemp += ' ';  
                else if (str[i] == '%')  
                {  
                    assert(i + 2 < length);  
                    unsigned char high = FromHex((unsigned char)str[++i]);  
                    unsigned char low = FromHex((unsigned char)str[++i]);  
                    strTemp += high*16 + low;  
                }  
                else strTemp += str[i];  
            }  
            return strTemp;  
        }

        static bool ToJson(std::string& json_string, const google::protobuf::Message& msg)
        {
            google::protobuf::util::JsonPrintOptions options;
            options.add_whitespace = false;
            options.always_print_primitive_fields = false;
            options.preserve_proto_field_names = true;
            options.always_print_enums_as_ints = true;
            return (google::protobuf::util::Status::OK == google::protobuf::util::MessageToJsonString(msg, &json_string, options));
        }
        static bool FromJson(google::protobuf::Message& msg, const std::string& json_string)
        {
            google::protobuf::util::JsonParseOptions options;
            return (google::protobuf::util::Status::OK == google::protobuf::util::JsonStringToMessage(json_string, &msg, options));
        }

        static std::string ToStr(const Json::Value& msg)
        {
            Json::FastWriter writer;
            writer.omitEndingLineFeed();
            return writer.write(msg);
        }

        static bool FromStr(Json::Value& msg, const std::string& json_string)
        {
            Json::Reader reader; 
            return reader.parse(json_string, msg);
        }
        
        template<typename T>
        static std::string ToString(const std::vector<T>& group, const std::string& sep = ",")
        {
            std::stringstream ss;
            for (int i = 0; i < group.size(); i++)
            {
                if (i != 0) ss << sep;
                ss << group[i];
            }
            return ss.str();
        }
        template<typename T>
        static std::string ToGroupString(const T& group, const std::string& sep = ",")
        {
            std::stringstream ss;
            bool is_first = true;
            for (auto item : group)
            {
                if (is_first)
                {
                    is_first = false;
                }
                else
                {
                    ss << sep;
                }
                ss << item;
            }
            return ss.str();
        }
        template<typename T>
        static std::string ToMapString(const T& group, const std::string& kv_sep = ":", const std::string& item_sep = ",")
        {
            std::stringstream ss;
            bool is_first = true;
            for (auto item : group)
            {
                if (is_first)
                {
                    is_first = false;
                }
                else
                {
                    ss << item_sep;
                }
                ss << item.first << kv_sep << item.second;
            }
            return ss.str();
        }

        static std::string  genTransId(void)
        {
            char sztmp[56] = { 0 };
            static std::uint32_t s_utransseq = 0; 
			while (!(s_utransseq = ++s_utransseq % std::numeric_limits<unsigned int>::max()))
			{
			}
            ::snprintf(sztmp, sizeof(sztmp), "%u_%llu_%u", TGlobal::_svid, time(NULL), s_utransseq);

            return std::string(sztmp);
        }
};

#endif